#include <system.h>
#include "globalopts.h"
#include "gc_menu.h"


unsigned char gm_ctl_flags;
unsigned char gm_menu_lvl;
unsigned short gm_menu_sel;
unsigned short gm_debounce;
unsigned short gm_max_count;
unsigned char gm_step;

unsigned char gm_timeout_sec;		//counts seconds to display menu

#define DISPLAY_TIME	20	//Cancel menu after 20 idle seconds

#define _GREY_SWITCH	((1 << GREY_PIN1) | (1 << GREY_PIN2))

#ifdef GREY1
#define grey_settled() (((portb & _GREY_SWITCH) == _GREY_SWITCH) || \
                      ((portb & _GREY_SWITCH) == 0x00))
#else
#define grey_settled() ((portb & _GREY_SWITCH) == _GREY_SWITCH)
#endif

#define button_press() (! BUTTON_PRESS_PIN)

inline void reset_grey_state()
{
	clear_bit(gm_ctl_flags, CLOCKWISE);
	clear_bit(gm_ctl_flags, GREY);
}

void set_grey_state()
{
	if((portb & _GREY_SWITCH) == 0x00) {
		clear_bit(gm_ctl_flags, GREY_HI);
	} else {
		set_bit(gm_ctl_flags, GREY_HI);
	}
}

void init_menu(void)
{
	gm_debounce = 0;
	gm_ctl_flags = 0;
	gm_menu_lvl = 0;
	gm_menu_sel = 0;
	reset_grey_state();
	set_grey_state();
}
//NOTE: interrupt handler must clear RBIF after calling check_gray_int()
void check_grey_int(void)
{
	char buf_portb;
	if (test_bit(intcon, RBIF)) {
		buf_portb = portb;				//Must test portb in order to clear the interrupt	
		if (! test_bit(gm_ctl_flags, GREY)) {
			if(!test_bit(buf_portb, GREY_PIN1)
			   && test_bit(buf_portb, GREY_PIN2))
			{
				if(test_bit(gm_ctl_flags, GREY_HI))
					set_bit(gm_ctl_flags, CLOCKWISE);
				set_bit(gm_ctl_flags, GREY);
			} else if(!test_bit(buf_portb, GREY_PIN2)
			          && test_bit(buf_portb, GREY_PIN1))
			{
				if(! test_bit(gm_ctl_flags, GREY_HI))
					set_bit(gm_ctl_flags, CLOCKWISE);
				set_bit(gm_ctl_flags, GREY);
			}
		}
		//clear_bit(intcon, RBIF);		//Let the interrupt handler clear this in case others care about RBIF
	}
}

//Timeouts assume this routine gets called every 32.7msec
void menu_callback(void)
{
	if(test_bit(gm_ctl_flags, BUTTON_DEBOUNCE)) {
		//~0.5 sec
		if(++gm_debounce >= 16) {
			gm_debounce = 0;
			clear_bit(gm_ctl_flags, BUTTON_DEBOUNCE);
		}
	}
}


void menu_update_1sec(void)
{
	if(test_bit(gm_ctl_flags, IN_MENU))
	{
		if(test_bit(gm_ctl_flags, RESET_COUNTDOWN))
		{
			clear_bit(gm_ctl_flags, RESET_COUNTDOWN);
			gm_timeout_sec=DISPLAY_TIME;
#ifdef MENU_RESET_CALLBACK
			menu_reset();
#endif
		}
	}
	if(0==--gm_timeout_sec) {
		clear_bit(gm_ctl_flags, IN_MENU);
	}
#ifdef MENU_1SEC_CALLBACK
	menu_1sec();
#endif
}

void menu_update_1min(void)
{
#ifdef MENU_1MIN_CALLBACK
	menu_1min();
#endif
}

void menu_loop(void)
{
	if(test_bit(gm_ctl_flags, BUTTON_DOWN))		//Button was previously pressed
	{
		if(test_bit(gm_ctl_flags, BUTTON_DEBOUNCE))	//Wait for debounce
			return;
		if(! button_press())				//Button was released
		{
			clear_bit(gm_ctl_flags, BUTTON_DOWN);
			clear_bit(gm_ctl_flags, GREY);
		} else {					//Button is still down
			set_bit(gm_ctl_flags, BUTTON_DEBOUNCE);
		}

		return;
	}
	if(test_bit(gm_ctl_flags, GREY) && grey_settled())	//The knob has been rotated
	{
		set_bit(gm_ctl_flags, RESET_COUNTDOWN);
		set_grey_state();								//Determine whether grey-code state is high or low
		if(gm_menu_lvl) {
			if(test_bit(gm_ctl_flags, CLOCKWISE))
			{
				if(gm_menu_sel < gm_max_count-gm_step) 
					gm_menu_sel += gm_step;
				else
					gm_menu_sel = gm_max_count;
			} else {
				if(gm_menu_sel > gm_step)
					gm_menu_sel -= gm_step;
				else
					gm_menu_sel = 0;
			}
			show_menu();
		}
		clear_bit(gm_ctl_flags, CLOCKWISE);
		clear_bit(gm_ctl_flags, GREY);
		return;
	}
	if(button_press())
	{
		char offset;
		set_bit(gm_ctl_flags, RESET_COUNTDOWN);
		set_bit(gm_ctl_flags, BUTTON_DOWN);
		set_bit(gm_ctl_flags, BUTTON_DEBOUNCE);
		if(! test_bit(gm_ctl_flags, IN_MENU)) {	//Initialize menu here as it may have been cleared by a timeout interrupt
			gm_menu_lvl = 0;
			set_bit(gm_ctl_flags, IN_MENU);
		}
		set_menu_state();
		if(! gm_menu_lvl) {						//Clear menu bit if the menu was closed 
			clear_bit(gm_ctl_flags, IN_MENU);
		} else {
			show_menu();
		}
	}
}
